/**
 * 文件下载模块
 * @module $ui/utils/download
 */

import { service } from './axios';
import { date } from './date';
import type { AxiosInstance, AxiosResponse, ResponseType } from 'axios';

/**
 * 通用post下载方法
 * url请求地址必须传
 * fileName文件名称
 * data请求参数
 * currency是否默认添加时间戳
 */
type DownloadOptions = {
	url?: string;
	fileName?: string;
	data?: any;
	currency?: boolean;
	a?: boolean;
};
export function downloadPost({ url, fileName, data, currency = false, a = false }: DownloadOptions = {}) {
	if (!url) {
		ElMessage({
			message: '下载地址不能为空！',
			type: 'error'
		});
		return;
	}
	const options = { responseType: 'blob' as ResponseType };
	const loadingInstance = ElLoading.service({ text: '下载中...' });
	return (service as AxiosInstance)
		.post(url, data, options)
		.then((response: AxiosResponse) => {
			loadingInstance.close();
			if (response.data.size == 0)
				return ElMessage({
					message: '暂无数据，导出失败！',
					type: 'error'
				});

			const contentDisposition = response?.headers?.['content-disposition'];
			const FN = contentDisposition ? decodeURI(contentDisposition.split('=')[1]) || fileName : fileName;
			if (a) {
				download(window.URL.createObjectURL(response.data), (currency ? `${FN}-${date(new Date())}` : FN)!);
			} else {
				downloadBlob(response.data, (currency ? `${FN}-${date(new Date())}` : FN)!);
			}
			ElMessage({
				message: '导出成功！',
				type: 'success'
			});
		})
		.catch(() => {
			ElMessage({
				message: '导出失败！',
				type: 'error'
			});
		});
}

/**
 * 通用get下载方法
 * url请求地址必须传
 * fileName文件名称
 * params请求参数
 * currency是否默认添加时间戳
 * 是否使用a标签下载
 */
export function downloadGet({ url, fileName, data, currency = false, a = false }: DownloadOptions = {}) {
	if (!url) {
		ElMessage({
			message: '下载地址不能为空！',
			type: 'error'
		});
		return;
	}
	const options = {
		params: data,
		responseType: 'blob' as ResponseType
	};
	const loadingInstance = ElLoading.service({ text: '下载中...' });
	return (service as AxiosInstance)
		.get(url, options)
		.then((response: AxiosResponse) => {
			loadingInstance?.close();
			if (response.data.size == 0)
				return ElMessage({
					message: '暂无数据，导出失败！',
					type: 'error'
				});

			const contentDisposition = response?.headers?.['content-disposition'];
			const FN = contentDisposition ? decodeURI(contentDisposition.split('=')[1]) || fileName : fileName;
			if (a) {
				download(window.URL.createObjectURL(response.data), (currency ? `${FN}-${date(new Date())}` : FN)!);
			} else {
				downloadBlob(response.data, (currency ? `${FN}-${date(new Date())}` : FN)!);
			}
			ElMessage({
				message: '导出成功！',
				type: 'success'
			});
		})
		.catch(() => {
			ElMessage({
				message: '导出失败！',
				type: 'error'
			});
		});
}

/**
 * 文件类型枚举 文件后缀名 -> base64 data:前缀
 * @readonly
 * @enum {string}
 */
export const TYPE_MAP = {
	/**
	 * data:image/jpg;base64,
	 */
	jpg: 'data:image/jpg;base64,',

	/**
	 * data:image/png;base64,
	 */
	png: 'data:image/png;base64,',
	/**
	 * data:text/csv;charset=utf-8,
	 */
	csv: 'data:text/csv;charset=utf-8,'
};

/**
 * 将字符串转换成Blob数据
 * @param str 字符串
 * @example
 *  let json = {name: '张三'};
 *  downloadByData(null, 'name.json', stringToBlob(JSON.stringify(json)))
 */
export function stringToBlob(str: string) {
	return URL.createObjectURL(new Blob([str], { type: 'application/octet-stream' }));
}

/**
 * base64数据转文件下载
 * @param type 文件类型
 * @param [saveName] 保存文件名
 * @param data  base64 数据
 */
export function downloadByData(type: 'jpg' | 'png' | 'csv' | null, saveName: string, data: string) {
	const link = document.createElement('a');
	link.download = saveName;
	link.href = type === null ? data : (TYPE_MAP[type] || '') + data;
	link.click();
}

/**
 * 下载文件
 * @param src 文件资源src
 * @param {string} [outName] 下载保存文件名称，可选
 */
export function download(src: string, outName: string) {
	const link = document.createElement('a');
	link.download = outName;
	link.href = src;
	document.body.appendChild(link);
	link.click();
	document.body.removeChild(link);
}

/**
 * 下载二进制文件
 * @param {Blob} blob 文件流
 * @param {string} [outName] 下载保存文件名称，可选
 */
export function downloadBlob(blob: Blob, outName: string) {
	const reader = new FileReader();
	reader.onload = (e) => {
		if (e.target?.result) download(e.target.result as string, outName);
	};
	reader.readAsDataURL(blob);
}

/**
 * post数据方式下载文件
 * @param {string} url 接口地址url
 * @param {object} [params] 发送的数据params
 * @param {string} [method=post] 请求方法：post / get
 */
export function downloadByPost(url: string, params: Record<string, any> = {}, method = 'post') {
	const form = document.createElement('form');
	form.style.display = 'none';
	form.action = url;
	form.method = method;
	form.target = '_blank';
	Object.keys(params).forEach((name) => {
		const input = document.createElement('input');
		input.type = 'hidden';
		input.name = name;
		input.value = params[name];
		form.appendChild(input);
	});
	document.body.appendChild(form);
	form.submit();
	document.body.removeChild(form);
}

/**
 *base64文件转文件流
 */
export function base64ToFile(data: string) {
	const arr = data.split(','),
		mime = arr[0].match(/:(.*?);/)?.[1],
		// suffix = mime.split('/')[1],
		bstr = atob(arr[1]);
	let n = bstr.length;
	const u8arr = new Uint8Array(n);

	while (n--) {
		u8arr[n] = bstr.charCodeAt(n);
	}
	return new Blob([u8arr], { type: mime });
}

/**
 * arrayBuffer转base64
 * @param buffer 缓存
 * @returns base64
 */
export function arrayBufferToBase64(buffer: ArrayBuffer) {
	let binary = '';
	const bytes = new Uint8Array(buffer);
	const len = bytes.byteLength;
	for (let i = 0; i < len; i++) {
		binary += String.fromCharCode(bytes[i]);
	}
	return window.btoa(binary);
}

/**
 * 判断base64的文件类型
 * @param data base64数据
 * @returns 文件类型
 */
export function base64FileType(data: string) {
	const fileHeader = new Map();
	//获取不同文件的文件头前3个字作为判断依据
	fileHeader.set('/9j', 'jpg');
	fileHeader.set('iVB', 'png');
	fileHeader.set('Qk0', 'mbp');
	fileHeader.set('SUk', 'tiff');
	fileHeader.set('JVB', 'pdf');
	fileHeader.set('UEs', 'ofd');
	let res = '';
	//遍历map中所提及的文件头特征
	fileHeader.forEach((v, k) => {
		if (k == data.slice(0, 3)) {
			res = v;
		}
	});
	//如果不在map中返回unknown file
	if (res == '') {
		res = 'unknown file';
	}
	//否则返回map中的value值
	return res;
}
